package br.org.bertol.orion.admin.internal;

import java.io.File;
import java.net.URL;
import java.util.Iterator;
import java.util.Map;
import java.util.Vector;
import javax.management.ObjectName;
import javax.naming.Context;
import javax.naming.NameNotFoundException;
import oracle.oc4j.admin.internal.ApplicationUnDeployer;
import oracle.oc4j.admin.internal.DeployerException;
import oracle.oc4j.admin.internal.DeployerStateKeeper;
import oracle.oc4j.admin.internal.WebApplicationBinder;
import oracle.oc4j.admin.jmx.shared.util.DataSink;
import oracle.oc4j.admin.jmx.shared.util.ObjectNameFactory;
import br.org.bertol.orion.admin.OrionApplicationServerAdministrator;
import com.evermind.naming.ContextUtils;
import com.evermind.naming.archive.MemoryArchiveContext;
import com.evermind.server.Application;
import com.evermind.server.ApplicationConfigReference;
import com.evermind.server.ApplicationServer;
import com.evermind.server.OC4JMonitorThread;
import com.evermind.server.OC4JServer;
import com.evermind.server.XMLServerConfig;
import com.evermind.server.deployment.EnterpriseArchive;
import com.evermind.server.deployment.WebModule;
import com.evermind.server.ejb.compilation.Compilation;
import com.evermind.server.http.deployment.HttpApplicationDescriptor;
import com.evermind.util.ConfigUtils;
import com.evermind.util.ExtendedFile;

public class OrionApplicationDeployer
    extends OrionDeployerBase
{

	private Application _previousApp;
	private Application _application;
	private ApplicationConfigReference _appConfigReference;
	private String _parentName;
	private Application _parentApp;
	private String _appDirPath;
	private String _deploymentDirPath;
	private String _archiveFileName;
	private String _archiveFilePath;
	private URL _archiveURL;
	private URL _expandURL;
	private URL _deploymentURL;
	private Vector _webModulePaths;
	private XMLServerConfig _parentConfig;
	private EnterpriseArchive _archive;
	private boolean _enableIIOP;
	private boolean _returnClientJar;
	private byte _clientJarBytes[];
	private boolean _removeEar;

	public OrionApplicationDeployer (ApplicationServer server, DataSink sink,
	    String deploymentName, Map extraParams)
	{
		super(server, sink, deploymentName, extraParams);
	}

	protected void doDeploy (boolean isReDeploy) throws DeployerException
	{
        Object obj = OrionApplicationServerAdministrator.iASDeploymentTransactionMutex_;
		
        synchronized (obj)
        {
    		try
    		{
    			if (OC4JServer.getOPMNEnabled()) OC4JMonitorThread.disableOpmnPing();
    			initVars();
    			undeployPrevious();
    			checkCancelState();
    			copyArchive();
    			unpackArchive();
    			checkCancelState();
    			initArchive();
    			initApplication();
    			initAppClientContext();
    			checkCancelState();
    			initWebModule();
    			addApplication();
    			storeConfig();
    			bindWebApp();
    			// removeEar();
    			setReturnValues();
    			if (OC4JServer.getOPMNEnabled()) OC4JMonitorThread.enableOpmnPing();

    		}
    		catch (Exception exception)
    		{

    			if (OC4JServer.getOPMNEnabled()) OC4JMonitorThread.enableOpmnPing();
    			throw new DeployerException(exception);
    		}
        }

	}

	protected void undo ()
	{
		try
		{
			if (_appServer.getApplication(_deploymentName, null) != null)
				try
				{
					ApplicationUnDeployer undeployer = new ApplicationUnDeployer(_appServer,
					    _deploymentName);
					undeployer.execute();
				}
				catch (Exception e)
				{
					removeFiles();
				}
			else
				removeFiles();
		}
		catch (Exception e)
		{
			System.err.println("WARNING: undo is not successful: " + e.getMessage());
		}
	}

	protected Object getNotificationSource ()
	{
		return "Application Deployer";
	}

	protected ObjectName getTargetObjectName ()
	{
		return ObjectNameFactory.create("oc4j:j2eeType=J2EEApplication,name=" + _deploymentName
		    + ",J2EEServer=" + "standalone");
	}

	private void undeployPrevious ()
	{
		if (getParam("undeployPrevious") == null || !"true".equals(getParam("undeployPrevious")))
		{
			sendNotification("Do not undeploy previous deployment");
			return;
		}
		try
		{
			sendNotification("Undeploy previous deployment");
			Application previous = _appServer.getApplication(_deploymentName, null);
			if (previous != null)
			{
				ApplicationUnDeployer undeployer = new ApplicationUnDeployer(_appServer,
				    _deploymentName);
				undeployer.setKeepWebBindings(true);
				undeployer.execute();
			}
			else
			{
				removeFiles(true);
			}
		}
		catch (Exception e)
		{
			System.err.println("WARNING: unable to undeploy the previous deployment for application "
			    + _deploymentName + " : " + e.getMessage());
			removeFiles(true);
		}
	}

	private void initVars () throws DeployerException
	{
		try
		{
			_previousApp = _appServer.getApplication(_deploymentName, null);
			_parentName = getParam("parent");
			_parentApp = _parentName != null
			    ? _appServer.getApplication(_parentName, null)
			    : _appServer.getDefaultApplication();
			if (_parentApp == null)
			    throw new DeployerException("parent application (" + _parentName != null
			        ? _parentName + ") is not found!"
			        : "default");
			_enableIIOP = "true".equals(getParam("enableIIOP")) && Compilation.iiopGen;
			_returnClientJar = "true".equals(getParam("returnClientJar")) && Compilation.iiopGen;
			_removeEar = "true".equals(getParam("removeEar"));
			if (_returnClientJar) _enableIIOP = true;
			String targetPath = getParam("targetPath");
			_appDirPath = targetPath != null
			    ? targetPath
			    : _appServer.getConfig().getApplicationDirectory();
			_archiveFileName = _deploymentName + ".ear";
			_archiveFilePath = _appDirPath + File.separator + _archiveFileName;
			_archiveURL = (new File(_archiveFilePath)).toURL();
			_parentConfig = (XMLServerConfig)_appServer.getConfig().getDefaultApplicationConfig();
			_deploymentDirPath = getParam("deploymentDirectory");
			if (_deploymentDirPath == null)
			    _deploymentDirPath = _appServer.getConfig().getDeploymentDirectory();
			_deploymentURL = ConfigUtils.getURL(_appServer.getConfig().getURL(), _deploymentDirPath
			    + File.separator + _deploymentName);
		}
		catch (DeployerException de)
		{
			throw (DeployerException)de.fillInStackTrace();
		}
		catch (Exception e)
		{
			throw new DeployerException(e);
		}
	}

	private void copyArchive () throws DeployerException
	{
		try
		{
			sendNotification("Copy the archive to " + _archiveFilePath);
			File dest = new File(_appDirPath, _archiveFileName);
			_sink.copyOrMoveTo(dest);
		}
		catch (Exception e)
		{
			throw new DeployerException(e);
		}
	}

	private void unpackArchive () throws DeployerException
	{
		try
		{
			sendNotification("Unpack " + _archiveFileName + " begins...");
			_expandURL = ApplicationServer.autoUnpack(_archiveURL, true, null);
			sendNotification("Unpack " + _archiveFileName + " ends...");
		}
		catch (Exception e)
		{
			throw new DeployerException(e);
		}
	}

	private void initArchive () throws DeployerException
	{
		try
		{
			sendNotification("Initialize " + _archiveFileName + " begins...");
			_archive = EnterpriseArchive.getArchive(_deploymentName, _expandURL, _parentConfig,
			    true);
			_archive.setExtraParams(_extraParams);
			_archive.setArchiveURL(_archiveURL);
			_archive.setContext(ContextUtils.getContext(_expandURL, true));
			_archive.setDeploymentURL(_deploymentURL);
			_archive.init();
			String data = getParam("<main>");
			if (data != null)
				_archive.initDeployment(data.getBytes());
			else
				_archive.initDeployment();
			if ("true".equals(getParam("autoCreateTables"))) _archive.setAutoCreateTables(true);
			if (_previousApp == null) _archive.initDefaultSettings();
			sendNotification("Initialize " + _archiveFileName + " ends...");
		}
		catch (Exception e)
		{
			throw new DeployerException(e);
		}
	}

	private void initApplication () throws DeployerException
	{
		try
		{
			String targetPath = getParam("targetPath");
			String appDirPath = targetPath != null
			    ? targetPath
			    : _appServer.getConfig().getApplicationDirectoryString();
			_appConfigReference = new ApplicationConfigReference(_archiveURL, _deploymentName,
			    _archiveFileName, _appServer.getConfig().getAutoStartApplications(), appDirPath,
			    _parentName);
			if (getParam("deploymentDirectory") != null)
			    _appConfigReference.setDeploymentDirectory(_appServer.getConfig().getURL(),
			        _deploymentDirPath + File.separator + _deploymentName);
			if (_enableIIOP) _appConfigReference.setEnableIIOP(true);
			_appConfigReference.setConfig(_archive);
			_application = new Application(_appServer, _parentApp, _appServer.isSecure(), null,
			    _appConfigReference);
			_application.setRemoveWebModuleTempDir();
		}
		catch (Exception e)
		{
			throw new DeployerException(e);
		}
	}

	private void initAppClientContext () throws DeployerException
	{
		if (_enableIIOP)
		{
			try
			{
				Context ctx = _archive.getDeploymentContext();
				byte clientData[] = new byte[0];
				MemoryArchiveContext clientJarContext = new MemoryArchiveContext(ctx,
				    "_iiopClient.jar", clientData, 0, clientData.length, false);
				ctx.rebind("_iiopClient.jar", clientData);
				ContextUtils.flush(clientJarContext);
			}
			catch (Exception e)
			{
				throw new DeployerException("Unable to read deployment directory: "
				    + e.getMessage());
			}
		}
		else
		{
			try
			{
				Context ctx = _archive.getDeploymentContext();
				ctx.unbind("_iiopClient.jar");
			}
			catch (Exception e)
			{
				throw new DeployerException(e);
			}
		}
	}

	private void initWebModule () throws DeployerException
	{
		try
		{
			_webModulePaths = new Vector();
			WebModule module;
			for (Iterator iterator = _archive.getWebApplications().iterator(); iterator.hasNext(); sendNotification("Initialize "
			    + module.getPath() + " ends..."))
			{
				module = (WebModule)iterator.next();
				String path = module.getPath();
				if (path.endsWith(".war"))
					System.out.println("WARNING: OrionApplicationDeployer.initWebModule(), path should NOT be ends with .war: "
					    + path + "!");
				else
					path = path + ".war";
				_webModulePaths.add(ConfigUtils.getURL(_archive.getURL(), path).getPath());
				sendNotification("Initialize " + module.getPath() + " begins...");
				HttpApplicationDescriptor webApp = new HttpApplicationDescriptor(_archive,
				    module.getName(), null, null, false);
				webApp.setContext(ContextUtils.getContext(_archive.getContext(), module.getPath(),
				    false));
				webApp.init();
				if (webApp.getExplicitName() == null) webApp.setName(module.getName());
				Context deploymentContext = _archive.getDeploymentContext();
				Context webContext = null;
				try
				{
					webContext = (Context)deploymentContext.lookup(module.getName());
				}
				catch (NameNotFoundException e)
				{
					webContext = deploymentContext.createSubcontext(module.getName());
				}
				webApp.setDeploymentContext(webContext);
				webApp.initDeployment();
				String data = getParam(module.getPackagedPath());
				if (data != null) webApp.initDeployment(data.getBytes());
				webApp.storeDeployment();
			}

		}
		catch (Exception e)
		{
			throw new DeployerException(e);
		}
	}

	private void addApplication () throws DeployerException
	{
		try
		{
			_appServer.addApplication(_archive, _application, null);
			_appServer.getConfig().addApplicationConfigReference(_appConfigReference);
		}
		catch (Exception e)
		{
			throw new DeployerException(e);
		}
	}

	private void storeConfig () throws DeployerException
	{
		try
		{
			_archive.storeDeployment();
			_appServer.getConfig().store();
		}
		catch (Exception e)
		{
			throw new DeployerException(e);
		}
	}

	private void bindWebApp () throws DeployerException
	{
		String siteName = getParam("bindWebApp");
		if (siteName != null)
		{
			WebApplicationBinder binder = new WebApplicationBinder(_appServer);
			sendNotification("bindWebApp to site " + siteName + " begins...");
			binder.bindWebApp(_deploymentName, siteName);
			sendNotification("bindWebApp to site " + siteName + " ends...");
		}
	}

	private void removeEar () throws DeployerException
	{
		if (!_removeEar) return;
		try
		{
			if (_archiveFilePath != null)
			{
				sendNotification("deleting:  " + _archiveFilePath);
				File earFile = new File(_archiveFilePath);
				if (earFile.exists()) earFile.delete();
			}
			if (_webModulePaths != null)
			{
				for (int n = 0; n < _webModulePaths.size(); n++)
				{
					String webModulePath = (String)_webModulePaths.elementAt(n);
					sendNotification("deleting:  " + webModulePath);
					File warFile = new File(webModulePath);
					if (warFile.exists()) warFile.delete();
				}

				_webModulePaths.removeAllElements();
				_webModulePaths = null;
			}
		}
		catch (Exception e)
		{
			System.err.println("WARNING: unable to remove ear and war files after deployment: "
			    + e.getMessage());
		}
	}

	private void setReturnValues () throws DeployerException
	{
		if (_returnClientJar)
		{
			try
			{
				Context ctx = _archive.getDeploymentContext();
				_clientJarBytes = (byte[])ctx.lookup("_iiopClient.jar");
			}
			catch (Exception e)
			{
				throw new DeployerException(e);
			}
			addReturnValue("<client-stubs-supported>", "true");
			addReturnValue("<return-client-jar>", _clientJarBytes);
		}
		else
		{
			addReturnValue("<client-stubs-supported>", "false");
		}
	}

	private void checkCancelState () throws DeployerException
	{
		Integer state = (Integer)DeployerStateKeeper.getState(_id);
		if (state == DeployerStateKeeper.CANCEL)
		{
			DeployerStateKeeper.setState(_id, DeployerStateKeeper.CANCELLED);
			throw new DeployerException("operation is cancelled");
		}
		else
		{
			return;
		}
	}

	private void removeFiles ()
	{
		removeFiles(false);
	}

	private void removeFiles (boolean force)
	{
		sendNotification("Removing files for app " + _archiveURL);
		File appDir = null;
		File deploymentDir = null;
		File appFile = null;
		try
		{
			if (_expandURL == null)
			{
				String name = _archiveURL.getFile();
				name = name.substring(0, name.lastIndexOf('.'));
				File target = new File(name);
				_expandURL = target.toURL();
			}
			appDir = new File(_expandURL.getFile());
			ExtendedFile.recursiveRemove(appDir);
		}
		catch (Exception e)
		{
			System.err.println("WARNING: Unable to remove appDir " + appDir.getAbsolutePath()
			    + " : " + e.getMessage());
		}
		try
		{
			appFile = new File(_archiveURL.getFile());
			if (appFile.exists())
			{
				boolean deleted = appFile.delete();
				if (!deleted)
				    System.err.println("WARNING: Unable to remove " + appFile.getAbsolutePath()
				        + "!");
			}
		}
		catch (Exception e)
		{
			System.err.println("WARNING: Unable to remove " + appFile.getAbsolutePath() + " : "
			    + e.getMessage());
		}
		try
		{
			deploymentDir = new File(_deploymentURL.getFile());
			ExtendedFile.recursiveRemove(deploymentDir);
		}
		catch (Exception e)
		{
			System.err.println("WARNING: Unable to remove " + deploymentDir.getAbsolutePath()
			    + " : " + e.getMessage());
		}
	}
}
